﻿using Styx;
using Styx.WoWInternals.WoWObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommonBehaviors.Actions;
using Styx.CommonBot;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.CommonBot.Coroutines;
using Buddy.Coroutines;

#region [Method] - Class Redundancy
using Action = Styx.TreeSharp.Action;
using L = SinisterD.Core.Utilities.Log;
using U = SinisterD.Core.Unit;
#endregion 

namespace SinisterD.Core
{
    static class Spell
    {
        public delegate WoWUnit unitSelectionDelegate(object Context); //why is this here?
        public delegate T Selection<out T>(object Context);
        public static int lastSpellCast;
        private static LocalPlayer Me { get { return StyxWoW.Me; } }
        private static WoWUnit currentTarget { get { return StyxWoW.Me.CurrentTarget; } }

        #region [Method] - Cooldown Detection
        public static double cooldownTimeLeft(this int Spell)
        {
            try
            {
                SpellFindResults Results;
                return SpellManager.FindSpell(Spell, out Results) ? (Results.Override != null ? Results.Override.CooldownTimeLeft.TotalMilliseconds : Results.Original.CooldownTimeLeft.TotalMilliseconds) : 9999;
            }
            catch (Exception xException)
            {
                L.diagnosticLog("Exception in cooldownTimeLeft(): ", xException);
                return 9999;
            }
        }
        public static bool onCooldown(int Spell)
        {
            try
            {
                SpellFindResults Results;
                if (SpellManager.FindSpell(Spell, out Results)) { return Results.Override != null ? Results.Override.Cooldown : Results.Original.Cooldown; }
                return false;
            }
            catch (Exception xException)
            {
                L.diagnosticLog("Exception in onCooldown(): ", xException);
                return false;
            }
        }
        #endregion

        #region [Method] - Spell Casting

        public static async Task<bool> Buff(int Spell, bool reqs)
        {
            if (!reqs)
                return false;
            if (!SpellManager.CanCast(WoWSpell.FromId(Spell), Me, false, false, false))
                return false;
            if (!SpellManager.Cast(Spell, Me))
                return false;
            lastSpellCast = Spell;
            L.combatLog("Casting: " + WoWSpell.FromId(Spell).Name);
            await CommonCoroutines.SleepForLagDuration();
            return true;
        }
        public static async Task<bool> Cast(int Spell,double Range, bool reqs)
        {
            if (!U.isUnitValid(currentTarget, Range))
                return false;
            if (!reqs)
                return false;
            if (!SpellManager.CanCast(WoWSpell.FromId(Spell), currentTarget, false, false, false))
                return false;
            if (!SpellManager.Cast(Spell, currentTarget))
                return false;
            lastSpellCast = Spell;
            L.combatLog("Casting: " + WoWSpell.FromId(Spell).Name);
            await CommonCoroutines.SleepForLagDuration();
            return true;
        }

        public static async Task<bool> Cast(string spellname, double Range, bool reqs)
        {
            if (!U.isUnitValid(currentTarget, Range))
                return false;
            if (!reqs)
                return false;
            if (!SpellManager.CanCast(spellname))
                return false;
            if (!SpellManager.Cast(spellname, Me.CurrentTarget))
                return false;
            //lastSpellCast = Spell;
            L.combatLog("Casting: " + spellname);
            await CommonCoroutines.SleepForLagDuration();
            return true;
        }

        public static async Task<bool> CastStatic(int Spell, double Range, bool reqs)
        {
            if (!U.isUnitValid(currentTarget, Range))
                return false;
            if (!reqs)
                return false;
            if (!SpellManager.CanCast(WoWSpell.FromId(Spell), currentTarget, false, true, false))
                return false;
            if (!SpellManager.Cast(Spell, currentTarget))
                return false;
            lastSpellCast = Spell;
            L.combatLog("Casting: " + WoWSpell.FromId(Spell).Name);
            await CommonCoroutines.SleepForLagDuration();
            return true;
        }


        #region [Method] - Drop cast //Disabled
        //public static async Task<bool> Drop(int Spell, bool reqs)
        //{
        //    if (!U.isUnitValid(currentTarget))
        //        return false;
        //    if (!reqs)
        //        return false;
        //    if (!SpellManager.CanCast(WoWSpell.FromId(Spell), currentTarget, false, false, false))
        //        return false;
        //    if (!SpellManager.Cast(Spell, currentTarget))
        //        return false;
        //    if (!await Coroutine.Wait(10, () => Me.CurrentPendingCursorSpell != null))
        //    {
        //        L.diagnosticLog("Exception in Cast(): Death and Decay failed to cast");
        //        return false;
        //    }
        //    SpellManager.ClickRemoteLocation(currentTarget.Location);
        //    lastSpellCast = Spell;
        //    L.combatLog("Casting: " + WoWSpell.FromId(Spell).Name);
        //    await CommonCoroutines.SleepForLagDuration();
        //    return true;
        //}
        #endregion

        public static async Task<bool> GCD(int Spell, bool reqs)
        {
            if (!reqs)
                return false;
            if (!SpellManager.Cast(Spell, Me))
                return false;
            lastSpellCast = Spell;
            L.combatLog("Casting: " + WoWSpell.FromId(Spell).Name);
            await CommonCoroutines.SleepForLagDuration();
            return true;
        }
        public static async Task<bool> UseItem(int itemID, bool reqs)
        {
            if (!reqs) return false;
            WoWItem itemSet = Me.BagItems.FirstOrDefault(h => h.Entry == itemID);
            if (itemSet == null || itemSet.CooldownTimeLeft.TotalMilliseconds > 0)
            {
                return false;
            }
            itemSet.Use();
            L.combatLog("Using: " + itemSet.Name );
            await CommonCoroutines.SleepForLagDuration();
            return true;
        }

        #endregion

        public static double GetAuraTimeLeft(int aura, WoWUnit onUnit)
        {
            if (onUnit != null)
            {
                var result = onUnit.GetAuraById(aura);
                if (result != null)
                {
                    if (result.TimeLeft.TotalSeconds > 0)
                        return result.TimeLeft.TotalSeconds;
                }
            }
            return 0;
        }


        public static TimeSpan GetAuraTimeLeft(this WoWUnit onUnit, string auraName, bool fromMyAura = true)
        {
            WoWAura wantedAura =
                onUnit.GetAllAuras().Where(a => a != null && a.Name == auraName && a.TimeLeft > TimeSpan.Zero && (!fromMyAura || a.CreatorGuid == StyxWoW.Me.Guid)).FirstOrDefault();

            return wantedAura != null ? wantedAura.TimeLeft : TimeSpan.Zero;
        }
    }
}